package com.qiandao.xuexitong.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiandao.xuexitong.dto.*;
import com.qiandao.xuexitong.entity.*;
import com.qiandao.xuexitong.entity.User;
import com.qiandao.xuexitong.enums.AttendanceTypeEnum;
import com.qiandao.xuexitong.exception.BusinessException;
import com.qiandao.xuexitong.handler.AttendanceDataWriteHandler;
import com.qiandao.xuexitong.handler.AttendanceExportHandler;
import com.qiandao.xuexitong.handler.AttendanceHeadStyleHandler;
import com.qiandao.xuexitong.mapper.*;
import com.qiandao.xuexitong.service.AttendanceTaskService;
import com.qiandao.xuexitong.service.ClassroomService;
import com.qiandao.xuexitong.service.IClassroomStudentService;
import com.qiandao.xuexitong.service.IAttendanceRecordService;
import com.qiandao.xuexitong.service.UserService;
import com.qiandao.xuexitong.util.UUIDUtil;
import com.qiandao.xuexitong.vo.AttendanceExportVO;
import com.qiandao.xuexitong.websocket.WebSocketServer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 签到任务信息表 服务实现类
 * </p>
 *
 * @author YouJiao
 * @since 2025-04-11
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AttendanceTaskServiceImpl extends ServiceImpl<AttendanceTaskMapper, AttendanceTask> implements AttendanceTaskService {

    private final ClassroomMapper classroomMapper;
    private final ClassroomStudentMapper classroomStudentMapper;
    private final AttendanceRecordMapper attendanceRecordMapper;
    private final ClassroomService classroomService;
    private final IClassroomStudentService classroomStudentService;
    private final IAttendanceRecordService attendanceRecordService;
    private final UserMapper userMapper;
    private final UserService userService;
    
    @Override
    public void updateExpiredTasks() {
        LocalDateTime now = LocalDateTime.now();
        log.info("开始检查过期任务，当前时间：{}", now);
        
        // 先查询出所有需要更新的任务
        LambdaQueryWrapper<AttendanceTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AttendanceTask::getStatus, 0)
                .lt(AttendanceTask::getEndTime, now);
        
        List<AttendanceTask> tasks = list(queryWrapper);
        log.info("找到{}个需要更新的任务", tasks.size());
        
        for (AttendanceTask task : tasks) {
            log.info("任务ID：{}，结束时间：{}，当前时间：{}", 
                task.getId(), task.getEndTime(), now);
        }
        
        // 更新条件：状态为未结束(0)且结束时间小于当前时间的任务
        LambdaUpdateWrapper<AttendanceTask> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(AttendanceTask::getStatus, 0)
                .lt(AttendanceTask::getEndTime, now)
                .set(AttendanceTask::getStatus, 1);
        
        boolean success = update(wrapper);
        log.info("更新任务状态：{}", success ? "成功" : "暂无需更新");
    }
    
    @Override
    @Transactional
    public QrCodeInfoDTO createQrCodeAttendanceTask(Long teacherId, CreateQrCodeAttendanceTaskDTO dto) {
        // 验证班级是否存在且属于该教师
        Classroom classroom = classroomMapper.selectOne(
                new LambdaQueryWrapper<Classroom>()
                        .eq(Classroom::getId, dto.getClassroomId())
                        .eq(Classroom::getTeacherId, teacherId)
        );
        if (classroom == null) {
            throw new BusinessException("班级不存在或无权限");
        }
        
        // 生成唯一的签到码
        String qrCode = UUIDUtil.generateShortUUID();
        
        // 创建签到任务
        AttendanceTask task = new AttendanceTask();
        task.setTitle(dto.getTitle());
        task.setClassroomId(dto.getClassroomId());
        task.setType(AttendanceTypeEnum.QR_CODE); // 1表示二维码签到
        task.setContent("{\"qrCode\":\"" + qrCode + "\"}");
        task.setStatus(0); // 0表示未结束
        task.setStartTime(dto.getStartTime());
        task.setEndTime(dto.getEndTime());
        task.setCreateTime(LocalDateTime.now());
        
        // 保存签到任务
        save(task);
        
        // 返回二维码信息
        QrCodeInfoDTO qrCodeInfo = new QrCodeInfoDTO();
        qrCodeInfo.setTaskId(task.getId());
        qrCodeInfo.setQrCode(qrCode);
        // 二维码内容格式：taskId:qrCode
        qrCodeInfo.setQrCodeUrl(task.getId() + ":" + qrCode);
        
        return qrCodeInfo;
    }
    
    @Override
    @Transactional
    public void qrCodeSignIn(Long studentId, QrCodeSignInDTO dto) {
        // 获取签到任务
        AttendanceTask task = getById(dto.getTaskId());
        if (task == null) {
            throw new BusinessException("签到任务不存在");
        }
        
        // 验证任务类型
        if (task.getType() != AttendanceTypeEnum.QR_CODE) {
            throw new BusinessException("不是二维码签到任务");
        }
        
        // 验证任务状态
        if (task.getStatus() == 1) {
            throw new BusinessException("签到任务已结束");
        }
        
        // 验证签到时间
        LocalDateTime now = LocalDateTime.now();
        if (now.isBefore(task.getStartTime())) {
            throw new BusinessException("签到未开始");
        }
        if (task.getEndTime() != null && now.isAfter(task.getEndTime())) {
            throw new BusinessException("签到已结束");
        }
        
        // 验证学生是否属于该班级
        ClassroomStudent classroomStudent = classroomStudentMapper.selectOne(
                new LambdaQueryWrapper<ClassroomStudent>()
                        .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
                        .eq(ClassroomStudent::getStudentId, studentId)
        );
        if (classroomStudent == null) {
            throw new BusinessException("不属于该班级");
        }
        
        // 验证是否已签到
        AttendanceRecord record = attendanceRecordMapper.selectOne(
                new LambdaQueryWrapper<AttendanceRecord>()
                        .eq(AttendanceRecord::getTaskId, task.getId())
                        .eq(AttendanceRecord::getStudentId, studentId)
        );
        if (record != null) {
            throw new BusinessException("已签到");
        }
        
        // 验证签到码
        String content = task.getContent();
        if (!content.contains(dto.getQrCode())) {
            throw new BusinessException("签到码错误");
        }
        
        // 创建签到记录
        record = new AttendanceRecord();
        record.setTaskId(task.getId());
        record.setStudentId(studentId);
        record.setStatus(1); // 1表示成功
        record.setSignTime(now);
        record.setExtraInfo("{\"qrCode\":\"" + dto.getQrCode() + "\"}");
        
        attendanceRecordMapper.insert(record);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createGestureAttendanceTask(Long teacherId, CreateGestureAttendanceTaskDTO dto) {
        // 验证教室是否存在且属于该教师
        Classroom classroom = classroomService.getById(dto.getClassroomId());
        if (classroom == null || !classroom.getTeacherId().equals(teacherId)) {
            throw new BusinessException("教室不存在或不属于该教师");
        }
        
        // 创建签到任务
        AttendanceTask task = new AttendanceTask();
        task.setClassroomId(dto.getClassroomId());
        task.setType(AttendanceTypeEnum.GESTURE);
        task.setContent(dto.getGestureSequence());
        task.setStatus(0);
        task.setTitle(dto.getTitle());
        task.setStartTime(dto.getStartTime());
        task.setEndTime(dto.getEndTime());
        task.setCreateTime(LocalDateTime.now());
        task.setUpdateTime(LocalDateTime.now());
        
        save(task);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void gestureSignIn(Long studentId, GestureSignInDTO dto) {
        // 验证签到任务是否存在且类型正确
        AttendanceTask task = getById(dto.getTaskId());
        if (task == null || task.getType() != AttendanceTypeEnum.GESTURE) {
            throw new BusinessException("签到任务不存在或类型错误");
        }
        
        // 验证任务状态和时间
        if (task.getStatus() == 1) {
            throw new BusinessException("签到已结束");
        }
        
        // 验证学生是否属于该班级
        ClassroomStudent classroomStudent = classroomStudentService.getOne(
            new LambdaQueryWrapper<ClassroomStudent>()
                .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
                .eq(ClassroomStudent::getStudentId, studentId)
        );
        if (classroomStudent == null) {
            throw new BusinessException("不属于该班级");
        }
        
        // 验证是否已签到
        AttendanceRecord record = attendanceRecordService.getOne(
            new LambdaQueryWrapper<AttendanceRecord>()
                .eq(AttendanceRecord::getTaskId, dto.getTaskId())
                .eq(AttendanceRecord::getStudentId, studentId)
        );
        if (record != null) {
            throw new BusinessException("已签到");
        }
        
        // 验证手势序列
        if (!task.getContent().equals(dto.getGestureSequence())) {
            throw new BusinessException("手势序列错误");
        }
        
        // 创建签到记录
        record = new AttendanceRecord();
        record.setTaskId(dto.getTaskId());
        record.setStudentId(studentId);
        record.setStatus(1);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        
        attendanceRecordService.save(record);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createLocationAttendanceTask(Long teacherId, CreateLocationAttendanceTaskDTO dto) {
        // 验证教室是否存在且属于该教师
        Classroom classroom = classroomService.getById(dto.getClassroomId());
        if (classroom == null || !classroom.getTeacherId().equals(teacherId)) {
            throw new BusinessException("教室不存在或不属于该教师");
        }
        
        // 构建位置信息JSON
        Map<String, Object> locationInfo = new HashMap<>();
        locationInfo.put("latitude", dto.getLatitude());
        locationInfo.put("longitude", dto.getLongitude());
        locationInfo.put("radius", dto.getRadius());
        
        // 创建签到任务
        AttendanceTask task = new AttendanceTask();
        task.setClassroomId(dto.getClassroomId());
        task.setType(AttendanceTypeEnum.LOCATION);
        task.setContent(JSON.toJSONString(locationInfo));
        task.setStatus(0);
        task.setTitle(dto.getTitle());
        task.setCreateTime(LocalDateTime.now());
        task.setStartTime(dto.getStartTime());
        task.setEndTime(dto.getStartTime().plusMinutes(dto.getDuration()));
        task.setUpdateTime(LocalDateTime.now());
        
        save(task);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void locationSignIn(Long studentId, LocationSignInDTO dto) {
        // 验证签到任务是否存在且类型正确
        AttendanceTask task = getById(dto.getTaskId());
        if (task == null || task.getType() != AttendanceTypeEnum.LOCATION) {
            throw new BusinessException("签到任务不存在或类型错误");
        }
        
        // 验证任务状态和时间
        if (task.getStatus() == 1) {
            throw new BusinessException("签到已结束");
        }
        
        // 验证学生是否属于该班级
        ClassroomStudent classroomStudent = classroomStudentService.getOne(
            new LambdaQueryWrapper<ClassroomStudent>()
                .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
                .eq(ClassroomStudent::getStudentId, studentId)
        );
        if (classroomStudent == null) {
            throw new BusinessException("不属于该班级");
        }
        
        // 验证是否已签到
        AttendanceRecord record = attendanceRecordService.getOne(
            new LambdaQueryWrapper<AttendanceRecord>()
                .eq(AttendanceRecord::getTaskId, dto.getTaskId())
                .eq(AttendanceRecord::getStudentId, studentId)
        );
        if (record != null) {
            throw new BusinessException("已签到");
        }
        
        // 解析位置信息
        Map<String, Object> locationInfo = JSON.parseObject(task.getContent());
        double targetLatitude = Double.parseDouble(locationInfo.get("latitude").toString());
        double targetLongitude = Double.parseDouble(locationInfo.get("longitude").toString());
        int radius = Integer.parseInt(locationInfo.get("radius").toString());
        
        // 计算距离
        double distance = calculateDistance(targetLatitude, targetLongitude, 
            dto.getLatitude(), dto.getLongitude());
        
        // 验证距离
        if (distance > radius) {
            throw new BusinessException("不在签到范围内");
        }
        
        // 创建签到记录
        record = new AttendanceRecord();
        record.setTaskId(dto.getTaskId());
        record.setStudentId(studentId);
        record.setStatus(1);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        
        attendanceRecordService.save(record);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createCodeAttendanceTask(Long teacherId, CreateCodeAttendanceTaskDTO dto) {
        // 验证教室是否存在且属于该教师
        Classroom classroom = classroomService.getById(dto.getClassroomId());
        if (classroom == null || !classroom.getTeacherId().equals(teacherId)) {
            throw new BusinessException("教室不存在或不属于该教师");
        }
        
        // 验证时间
        LocalDateTime now = LocalDateTime.now();
        if (dto.getStartTime().isBefore(now)) {
            throw new BusinessException("开始时间不能早于当前时间");
        }
        if (dto.getEndTime().isBefore(now)) {
            throw new BusinessException("结束时间不能早于当前时间");
        }
        if (dto.getEndTime().isBefore(dto.getStartTime())) {
            throw new BusinessException("结束时间不能早于开始时间");
        }
        
        // 验证签到码格式
        if (!dto.getCode().matches("\\d{6}")) {
            throw new BusinessException("签到码必须是6位数字");
        }
        
        // 创建签到任务
        AttendanceTask task = new AttendanceTask();
        task.setClassroomId(dto.getClassroomId());
        task.setType(AttendanceTypeEnum.CODE);
        task.setTitle(dto.getTitle());
        task.setContent("{\"code\":\"" + dto.getCode() + "\"}");
        task.setStatus(0);
        task.setStartTime(dto.getStartTime());
        task.setEndTime(dto.getEndTime());
        task.setCreateTime(now);
        
        save(task);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void codeSignIn(Long studentId, CodeSignInDTO dto) {
        // 验证签到任务是否存在且类型正确
        AttendanceTask task = getById(dto.getTaskId());
        if (task == null || task.getType() != AttendanceTypeEnum.CODE) {
            throw new BusinessException("签到任务不存在或类型错误");
        }
        
        // 验证任务状态和时间
        if (task.getStatus() == 1) {
            throw new BusinessException("签到已结束");
        }
        
        // 验证学生是否属于该班级
        ClassroomStudent classroomStudent = classroomStudentService.getOne(
            new LambdaQueryWrapper<ClassroomStudent>()
                .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
                .eq(ClassroomStudent::getStudentId, studentId)
        );
        if (classroomStudent == null) {
            throw new BusinessException("不属于该班级");
        }
        
        // 验证是否已签到
        AttendanceRecord record = attendanceRecordService.getOne(
            new LambdaQueryWrapper<AttendanceRecord>()
                .eq(AttendanceRecord::getTaskId, dto.getTaskId())
                .eq(AttendanceRecord::getStudentId, studentId)
        );
        if (record != null) {
            throw new BusinessException("已签到");
        }
        
        // 验证签到码
        Map<String, Object> content = JSON.parseObject(task.getContent());
        if (!content.get("code").equals(dto.getCode())) {
            throw new BusinessException("签到码错误");
        }
        
        // 创建签到记录
        record = new AttendanceRecord();
        record.setTaskId(dto.getTaskId());
        record.setStudentId(studentId);
        record.setStatus(1);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        
        attendanceRecordService.save(record);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createQuickAttendanceTask(Long teacherId, CreateQuickAttendanceTaskDTO dto) {
        // 验证教室是否存在且属于该教师
        Classroom classroom = classroomService.getById(dto.getClassroomId());
        if (classroom == null || !classroom.getTeacherId().equals(teacherId)) {
            throw new BusinessException("教室不存在或不属于该教师");
        }

        // 验证时间
        LocalDateTime now = LocalDateTime.now();
        if (dto.getStartTime().isBefore(now)) {
            throw new BusinessException("开始时间不能早于当前时间");
        }
        if (dto.getEndTime().isBefore(now)) {
            throw new BusinessException("结束时间不能早于当前时间");
        }
        if (dto.getEndTime().isBefore(dto.getStartTime())) {
            throw new BusinessException("结束时间不能早于开始时间");
        }

        // 创建签到任务
        AttendanceTask task = new AttendanceTask();
        task.setClassroomId(dto.getClassroomId());
        task.setType(AttendanceTypeEnum.NORMAL);
        task.setTitle(dto.getTitle());
        task.setContent("{}");
        task.setStatus(0);
        task.setStartTime(dto.getStartTime());
        task.setEndTime(dto.getEndTime());
        task.setCreateTime(now);

        save(task);

        // 获取班级所有学生ID
        List<ClassroomStudent> classroomStudents = classroomStudentService.list(
            new LambdaQueryWrapper<ClassroomStudent>()
                .eq(ClassroomStudent::getClassroomId, dto.getClassroomId())
        );

        List<Long> studentIds = classroomStudents.stream()
            .map(ClassroomStudent::getStudentId)
            .collect(Collectors.toList());

        // 构建通知数据
        StudentAttendanceTaskDTO notification = new StudentAttendanceTaskDTO();
        notification.setId(task.getId());
        notification.setClassroomId(task.getClassroomId());
        notification.setClassroomName(classroom.getName());
        notification.setTeacherId(teacherId);
        User user = userMapper.selectById(teacherId);
        notification.setTeacherName(user.getName());
        notification.setType(task.getType());
        notification.setStartTime(task.getStartTime());
        notification.setEndTime(task.getEndTime());
        notification.setStatus(0); // 未开始

        // 发送WebSocket通知
        WebSocketServer.sendMessageToUsers(studentIds, notification);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void quickSignIn(Long studentId, QuickSignInDTO dto) {
        // 验证签到任务是否存在且类型正确
        AttendanceTask task = getById(dto.getTaskId());
        if (task == null || task.getType() != AttendanceTypeEnum.NORMAL) {
            throw new BusinessException("签到任务不存在或类型错误");
        }
        
        // 验证任务状态和时间
        if (task.getStatus() == 1) {
            throw new BusinessException("签到已结束");
        }
        
        // 验证学生是否属于该班级
        ClassroomStudent classroomStudent = classroomStudentService.getOne(
            new LambdaQueryWrapper<ClassroomStudent>()
                .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
                .eq(ClassroomStudent::getStudentId, studentId)
        );
        if (classroomStudent == null) {
            throw new BusinessException("不属于该班级");
        }
        
        // 验证是否已签到
        AttendanceRecord record = attendanceRecordService.getOne(
            new LambdaQueryWrapper<AttendanceRecord>()
                .eq(AttendanceRecord::getTaskId, dto.getTaskId())
                .eq(AttendanceRecord::getStudentId, studentId)
        );
        if (record != null) {
            throw new BusinessException("已签到");
        }
        
        // 创建签到记录
        record = new AttendanceRecord();
        record.setTaskId(dto.getTaskId());
        record.setStudentId(studentId);
        record.setStatus(1);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        
        attendanceRecordService.save(record);
    }
    
    /**
     * 生成6位数字签到码
     */
    private String generateCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }
    
    /**
     * 计算两点之间的距离（米）
     * 使用Haversine公式
     */
    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        final int R = 6371; // 地球半径（千米）
        
        double latDistance = Math.toRadians(lat2 - lat1);
        double lonDistance = Math.toRadians(lon2 - lon1);
        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        double distance = R * c * 1000; // 转换为米
        
        return distance;
    }
    
    @Override
    public IPage<AttendanceTaskListDTO> getTeacherAttendanceTasks(Long teacherId, Page<AttendanceTask> page) {
        // 1. 查询教师的所有班级
        List<Classroom> classrooms = classroomService.list(
            new LambdaQueryWrapper<Classroom>()
                .eq(Classroom::getTeacherId, teacherId)
        );
        
        if (classrooms.isEmpty()) {
            Page<AttendanceTaskListDTO> emptyPage = new Page<>(page.getCurrent(), page.getSize());
            return emptyPage;
        }
        
        // 2. 获取所有班级ID
        List<Long> classroomIds = classrooms.stream()
            .map(Classroom::getId)
            .collect(Collectors.toList());
        
        // 3. 查询这些班级的所有签到任务
        Page<AttendanceTask> resultPage = page(page, 
            new LambdaQueryWrapper<AttendanceTask>()
                .in(AttendanceTask::getClassroomId, classroomIds)
                .orderByDesc(AttendanceTask::getCreateTime)
        );
        
        // 4. 转换为DTO分页
        return resultPage.convert(task -> {
            AttendanceTaskListDTO dto = new AttendanceTaskListDTO();
            // 复制基本属性
            dto.setId(task.getId());
            dto.setClassroomId(task.getClassroomId());
            dto.setType(task.getType().getCode());
            dto.setTitle(task.getTitle());
            dto.setContent(task.getContent());
            dto.setStartTime(task.getStartTime());
            dto.setEndTime(task.getEndTime());
            dto.setStatus(task.getStatus());
            dto.setCreateTime(task.getCreateTime());
            
            // 设置班级名称
            classrooms.stream()
                .filter(c -> c.getId().equals(task.getClassroomId()))
                .findFirst()
                .ifPresent(c -> dto.setClassroomName(c.getName()));
            
            // 查询班级学生总数
            long totalCount = classroomStudentService.count(
                new LambdaQueryWrapper<ClassroomStudent>()
                    .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
            );
            dto.setTotalCount((int) totalCount);
            
            // 查询已签到人数
            long signedCount = attendanceRecordService.count(
                new LambdaQueryWrapper<AttendanceRecord>()
                    .eq(AttendanceRecord::getTaskId, task.getId())
            );
            dto.setSignedCount((int) signedCount);
            
            // 计算签到率
            if (totalCount > 0) {
                double rate = (double) signedCount / totalCount * 100;
                dto.setSignRate(String.format("%.2f%%", rate));
            } else {
                dto.setSignRate("0.00%");
            }
            
            // 查询签到记录
            List<AttendanceRecord> records = attendanceRecordService.list(
                new LambdaQueryWrapper<AttendanceRecord>()
                    .eq(AttendanceRecord::getTaskId, task.getId())
            );
            
            // 查询学生信息
            List<Long> studentIds = records.stream()
                .map(AttendanceRecord::getStudentId)
                .collect(Collectors.toList());
            
            if (!studentIds.isEmpty()) {
                List<User> students = userService.listByIds(studentIds);
                Map<Long, String> studentNameMap = students.stream()
                    .collect(Collectors.toMap(User::getId, User::getName));
                
                // 构建签到记录详情列表
                List<AttendanceTaskListDTO.AttendanceRecordDetail> recordDetails = records.stream()
                    .map(record -> {
                        AttendanceTaskListDTO.AttendanceRecordDetail detail = new AttendanceTaskListDTO.AttendanceRecordDetail();
                        BeanUtils.copyProperties(record, detail);
                        detail.setStudentName(studentNameMap.get(record.getStudentId()));
                        return detail;
                    })
                    .collect(Collectors.toList());
                
                dto.setRecords(recordDetails);
            }
            
            return dto;
        });
    }
    
    @Override
    public AttendanceDetailDTO getAttendanceDetail(Long taskId) {
        // 1. 获取签到任务信息
        AttendanceTask task = getById(taskId);
        if (task == null) {
            throw new BusinessException("签到任务不存在");
        }
        
        // 2. 获取班级所有学生
        List<ClassroomStudent> classroomStudents = classroomStudentService.list(
            new LambdaQueryWrapper<ClassroomStudent>()
                .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
        );
        
        // 3. 获取所有学生ID
        List<Long> studentIds = classroomStudents.stream()
            .map(ClassroomStudent::getStudentId)
            .collect(Collectors.toList());
        
        // 4. 获取学生信息
        List<User> users = userMapper.selectBatchIds(studentIds);
        Map<Long, String> studentNameMap = users.stream()
            .collect(Collectors.toMap(User::getId, User::getName));
        
        // 5. 获取已签到记录
        List<AttendanceRecord> signedRecords = attendanceRecordService.list(
            new LambdaQueryWrapper<AttendanceRecord>()
                .eq(AttendanceRecord::getTaskId, taskId)
        );
        
        // 6. 获取已签到学生ID和签到时间、状态
        Map<Long, AttendanceRecord> signedStudentMap = signedRecords.stream()
            .collect(Collectors.toMap(
                AttendanceRecord::getStudentId,
                record -> record
            ));
        
        // 7. 分离已签到和未签到学生
        List<StudentAttendanceDTO> signedStudents = users.stream()
            .filter(student -> signedStudentMap.containsKey(student.getId()))
            .map(student -> {
                StudentAttendanceDTO dto = new StudentAttendanceDTO();
                dto.setId(student.getId());
                dto.setName(student.getName());
                AttendanceRecord record = signedStudentMap.get(student.getId());
                dto.setSignTime(record.getSignTime());
                dto.setStatus(record.getStatus());
                return dto;
            })
            .collect(Collectors.toList());
            
        List<StudentAttendanceDTO> unsignedStudents = users.stream()
            .filter(student -> !signedStudentMap.containsKey(student.getId()))
            .map(student -> {
                StudentAttendanceDTO dto = new StudentAttendanceDTO();
                dto.setId(student.getId());
                dto.setName(student.getName());
                dto.setStatus(2); // 未签到状态为2（缺勤）
                return dto;
            })
            .collect(Collectors.toList());
        
        // 8. 构建返回结果
        AttendanceDetailDTO detail = new AttendanceDetailDTO();
        detail.setTotalCount(users.size());
        detail.setSignedCount(signedStudents.size());
        detail.setUnsignedCount(unsignedStudents.size());
        
        // 计算签到率
        if (users.size() > 0) {
            double rate = (double) signedStudents.size() / users.size() * 100;
            detail.setSignRate(String.format("%.2f%%", rate));
        } else {
            detail.setSignRate("0.00%");
        }
        
        detail.setSignedStudents(signedStudents);
        detail.setUnsignedStudents(unsignedStudents);
        
        return detail;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAttendanceStatus(Long teacherId, UpdateAttendanceStatusDTO dto) {
        // 1. 验证签到任务是否存在且属于该教师
        AttendanceTask task = getById(dto.getTaskId());
        if (task == null) {
            throw new BusinessException("签到任务不存在");
        }
        
        // 验证班级是否属于该教师
        Classroom classroom = classroomService.getById(task.getClassroomId());
        if (classroom == null || !classroom.getTeacherId().equals(teacherId)) {
            throw new BusinessException("无权限修改该签到任务");
        }
        
        // 2. 验证学生是否属于该班级
        ClassroomStudent classroomStudent = classroomStudentService.getOne(
            new LambdaQueryWrapper<ClassroomStudent>()
                .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
                .eq(ClassroomStudent::getStudentId, dto.getStudentId())
        );
        if (classroomStudent == null) {
            throw new BusinessException("该学生不属于此班级");
        }
        
        // 3. 验证状态值是否合法
        if (dto.getStatus() != 0 && dto.getStatus() != 1 && dto.getStatus() != 2) {
            throw new BusinessException("无效的签到状态");
        }
        
        // 4. 查找签到记录
        AttendanceRecord record = attendanceRecordService.getOne(
            new LambdaQueryWrapper<AttendanceRecord>()
                .eq(AttendanceRecord::getTaskId, dto.getTaskId())
                .eq(AttendanceRecord::getStudentId, dto.getStudentId())
        );
        
        LocalDateTime now = LocalDateTime.now();
        
        // 5. 如果记录不存在，创建新记录
        if (record == null) {
            record = new AttendanceRecord();
            record.setTaskId(dto.getTaskId());
            record.setStudentId(dto.getStudentId());
            record.setStatus(dto.getStatus());
            record.setSignTime(now);
            record.setExtraInfo("{\"type\":\"teacher_manual\",\"teacherId\":" + teacherId + "}");
            attendanceRecordService.save(record);
        } else {
            // 6. 更新现有记录
            record.setStatus(dto.getStatus());
            record.setExtraInfo("{\"type\":\"teacher_manual\",\"teacherId\":" + teacherId + "}");
            attendanceRecordService.updateById(record);
        }
    }
    
    @Override
    public byte[] exportAttendanceDetail(Long taskId) {
        try {
            // 获取签到任务
            AttendanceTask task = getById(taskId);
            if (task == null) {
                throw new BusinessException("签到任务不存在");
            }
            
            // 获取班级信息
            Classroom classroom = classroomService.getById(task.getClassroomId());
            if (classroom == null) {
                throw new BusinessException("班级不存在");
            }
            
            // 获取教师信息
            User teacher = userMapper.selectById(classroom.getTeacherId());
            if (teacher == null) {
                throw new BusinessException("教师不存在");
            }
            
            // 获取班级所有学生
            List<ClassroomStudent> classroomStudents = classroomStudentService.list(
                new LambdaQueryWrapper<ClassroomStudent>()
                    .eq(ClassroomStudent::getClassroomId, task.getClassroomId())
            );
            
            // 获取所有学生ID
            List<Long> studentIds = classroomStudents.stream()
                .map(ClassroomStudent::getStudentId)
                .collect(Collectors.toList());
            
            // 获取所有学生信息
            List<User> students = userMapper.selectBatchIds(studentIds);
            
            // 获取签到记录
            List<AttendanceRecord> records = attendanceRecordService.lambdaQuery()
                    .eq(AttendanceRecord::getTaskId, taskId)
                    .list();
            
            // 获取已签到人数
            int signedCount = (int) records.stream()
                    .filter(record -> record.getStatus() == 1)
                    .count();
            
            // 构建签到记录Map
            Map<Long, AttendanceRecord> recordMap = records.stream()
                .collect(Collectors.toMap(AttendanceRecord::getStudentId, record -> record));
            
            // 准备导出数据
            List<AttendanceExportVO> exportData = new ArrayList<>();
            for (User student : students) {
                AttendanceExportVO vo = new AttendanceExportVO();
                vo.setName(student.getName());
                vo.setStuNum(student.getStuNum());
                vo.setSchool(student.getSchool());
                vo.setDepartment(student.getDepartment());
                vo.setProfession(student.getProfession());
                vo.setClassNum(student.getClassNum());
                
                // 查找学生的签到记录
                AttendanceRecord record = recordMap.get(student.getId());
                if (record == null) {
                    vo.setStatus("未签到");
                    vo.setSignTime(null);
                } else {
                    switch (record.getStatus()) {
                        case 0:
                            vo.setStatus("迟到");
                            break;
                        case 1:
                            vo.setStatus("已签到");
                            break;
                        case 2:
                            vo.setStatus("缺勤");
                            break;
                        default:
                            vo.setStatus("未签到");
                    }
                    vo.setSignTime(record.getSignTime());
                }
                exportData.add(vo);
            }
            
            // 导出Excel
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                Workbook workbook = new XSSFWorkbook();
                AttendanceExportHandler exportHandler = new AttendanceExportHandler(
                    workbook,
                    classroom.getName(),
                    teacher.getName(),
                    signedCount,
                    task.getStartTime() != null ? task.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : task.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                    task.getEndTime() != null ? task.getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : "",
                    task.getType().getDesc()
                );
                AttendanceDataWriteHandler dataWriteHandler = new AttendanceDataWriteHandler(exportHandler);
                AttendanceHeadStyleHandler headStyleHandler = new AttendanceHeadStyleHandler();
                
                EasyExcel.write(outputStream, AttendanceExportVO.class)
                        .registerWriteHandler(exportHandler)
                        .registerWriteHandler(dataWriteHandler)
                        .registerWriteHandler(headStyleHandler)
                        .sheet("签到记录")
                        .doWrite(exportData);
                return outputStream.toByteArray();
            } catch (Exception e) {
                log.error("导出Excel文件失败", e);
                throw new BusinessException("导出Excel文件失败：" + e.getMessage());
            }
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("导出签到记录Excel失败", e);
            throw new BusinessException("导出Excel失败：" + e.getMessage());
        }
    }
    
    @Override
    public List<StudentAttendanceTaskDTO> getStudentAttendanceTasks(Long studentId) {
        // 1. 查询学生所在的班级
        List<Classroom> classrooms = classroomService.getStudentClassrooms(studentId);
        if (classrooms.isEmpty()) {
            return Collections.emptyList();
        }
        
        // 2. 获取班级ID列表
        List<Long> classroomIds = classrooms.stream()
                .map(Classroom::getId)
                .collect(Collectors.toList());
        
        // 3. 查询这些班级的签到任务
        List<AttendanceTask> tasks = lambdaQuery()
                .in(AttendanceTask::getClassroomId, classroomIds)
                .orderByDesc(AttendanceTask::getCreateTime)
                .list();
        
        // 4. 转换为DTO并设置学生签到状态
        return tasks.stream().map(task -> {
            StudentAttendanceTaskDTO dto = new StudentAttendanceTaskDTO();
            BeanUtils.copyProperties(task, dto);
            
            // 设置班级名称
            classrooms.stream()
                    .filter(c -> c.getId().equals(task.getClassroomId()))
                    .findFirst()
                    .ifPresent(c -> {
                        dto.setClassroomName(c.getName());
                        // 设置教师ID
                        dto.setTeacherId(c.getTeacherId());
                        // 查询教师姓名
                        User teacher = userService.getById(c.getTeacherId());
                        if (teacher != null) {
                            dto.setTeacherName(teacher.getName());
                        }
                    });
            
            // 设置签到类型
            dto.setType(task.getType());
            
            // 设置签到状态
            LocalDateTime now = LocalDateTime.now();
            if (task.getStartTime() != null && now.isBefore(task.getStartTime())) {
                dto.setStatus(0); // 未开始
            } else if (task.getEndTime() != null && now.isAfter(task.getEndTime())) {
                dto.setStatus(2); // 已结束
            } else {
                dto.setStatus(1); // 进行中
            }
            
            // 查询学生签到记录
            AttendanceRecord record = attendanceRecordService.lambdaQuery()
                    .eq(AttendanceRecord::getTaskId, task.getId())
                    .eq(AttendanceRecord::getStudentId, studentId)
                    .one();
            
            if (record != null) {
                dto.setStudentStatus(record.getStatus());
                dto.setSignTime(record.getSignTime());
            } else {
                dto.setStudentStatus(0); // 未签到
            }
            
            return dto;
        }).collect(Collectors.toList());
    }
}
